<?php


namespace Rum\Router\Annotation;

use Doctrine\Common\Annotations\AnnotationReader;
use Doctrine\Common\Annotations\AnnotationRegistry;
use Fend\Router\RouterException;
use Fend\Request;
use Fend\Response;

use Rum\Router\Annotation\Controller;
use Rum\Router\Annotation\Router;
use Rum\Router\Annotation\Middleware;

/**
 * 注解解析
 */
class RouterParser
{
    /**
     * 根命名空间
     */
    private $controllerNamespace;
    /**
     * 控制器根目录
     */
    private $controllerBasePath;

    /**
     * 根中间件命名空间
     */
    private $middlewareNamespace;
    /**
     * 中间件根目录
     */
    private $middlewareBasePath;

    /**
     * 是否使用匿名函数
     */
    private $enableClosure;

    /**
     * 全局生效的中间件
     */
    private $globalMiddleware;

    /**
     * 中间件映射
     */
    private $middleware;

    /**
     * 路由注解解析
     * @param string $baseNamespace 路由根命名空间
     * @param string $controllerBasePath 路由php文件的根目录
     * @param string $controllerBasePath 中间件php文件的根目录
     * @param string[] $ignoreAnnotations 需要忽略的注解(比如@author,@date等)
     */
    public function __construct($opts)
    {
        if (empty($opts) || empty($opts['controllerNamespace']) || empty($opts['controllerBasePath']) || empty($opts['middlewareNamespace']) || empty($opts['middlewareBasePath'])) {
            throw new RouterException('router options illege');
        }
        // 注册类加载器
        AnnotationRegistry::registerLoader(function ($class) {
            return class_exists($class) || interface_exists($class);
        });

        // 添加需要忽略的注解
        $ignoreAnnotations = isset($opts['ignoreAnnotations']) && is_array($opts['ignoreAnnotations']) ? $opts['ignoreAnnotations'] : [];
        foreach ($ignoreAnnotations as $ignoreClassName) {
            AnnotationReader::addGlobalIgnoredName($ignoreClassName);
        }
        $this->controllerNamespace = $opts['controllerNamespace'];
        $this->controllerBasePath = $opts['controllerBasePath'];
        $this->middlewareNamespace = $opts['middlewareNamespace'];
        $this->middlewareBasePath = $opts['middlewareBasePath'];
        $this->enableClosure = $opts['enableClosure'] ?? false;
        $this->globalMiddleware = !empty($opts['globalMiddleware']) ? $opts['globalMiddleware'] : [];
    }

    /**
     * 解析所有的注解路由
     * @return GroupItem[] 路由组集合
     */
    public function handle()
    {
        try {
            // 加载中间件
            $this->middleware = $this->handleMiddlewareGroup($this->middlewareBasePath, $this->middlewareNamespace);
            // 加载路由
            return $this->handleRouterGroup($this->controllerBasePath, $this->controllerNamespace);
        } catch (\Throwable $th) {
            throw new RouterException('解析注解路由异常:' . $th->getMessage());
            return [];
        }
    }

    /**
     * 解析某目录下的所有路由
     * @param string $path 路径
     * @param string $workspace 命名空间
     * @return GroupItem[] 路由组集合
     */
    public function handleRouterGroup($path, $workspace)
    {
        if (!is_dir($path)) {
            return;
        }
        $dir = opendir($path);
        if ($dir === false) {
            throw new RouterException('can not open controller directory ' . $dir);
            return [];
        }
        $groups = [];
        while (($file = readdir($dir)) !== false) {
            if ($file[0] == '.' || $file == 'vendor') {
                // 忽略隐藏、vendor目录
                continue;
            }
            $absulatePath = $path . '/' . $file;
            if (is_dir($absulatePath)) {
                array_push($groups, ...$this->handleRouterGroup($absulatePath, $workspace . '\\' . $file, $file));
                continue;
            }
            $group = $this->handleRouter($absulatePath, $workspace);
            if (!is_null($group)) {
                array_push($groups, $group);
            }
        }
        return $groups;
    }

    /**
     * 解析Controller文件，读取所有routers
     * @param string $path 文件路径
     * @param string $workspace 命名空间
     * @return GroupItem 路由
     */
    public function handleRouter($path, $workspace)
    {
        $info = pathinfo($path);
        if ($info['extension'] !== 'php') {
            return [];
        }
        $className = $workspace . '\\' . $info['filename'];
        if (!class_exists($className)) {
            return [];
        }
        // 读取Controller注解
        $annotationReader = new AnnotationReader();
        $reflectionClass = new \ReflectionClass($className);
        $controllerAnnotation = $annotationReader->getClassAnnotation($reflectionClass, Controller::class);
        if (empty($controllerAnnotation)) {
            return null;
        }
        $group = new GroupItem($info['filename']);
        // 记录当前路由组的统一前缀路径
        $group->setPrefix($controllerAnnotation->prefix);
        $methods = $reflectionClass->getMethods();
        if (empty($methods)) {
            return $group;
        }
        // 添加全局路由
        if (!empty($this->globalMiddleware)) {
            foreach ($this->globalMiddleware as $v) {
                if (empty($this->middleware[$v])) {
                    continue;
                }
                $group->addMiddleware($this->middleware[$v]);
            }
        }
        // 记录当前路由组的中间件
        $middlewares = !empty($controllerAnnotation->middleware) ? explode(',', $controllerAnnotation->middleware) : [];
        if (!empty($middlewares)) {
            foreach ($middlewares as $v) {
                if (empty($this->middleware[$v])) {
                    continue;
                }
                $group->addMiddleware($this->middleware[$v]);
            }
        }
        $cont = $this->enableClosure ? new $className() : null;
        foreach ($methods as $method) {
            // 解析Router注解
            $routerAnnotations = $annotationReader->getMethodAnnotations($method);
            foreach ($routerAnnotations as $anno) {
                if (get_class($anno) != Router::class) {
                    continue;
                }
                $handles = [];
                $methodMids = !empty($anno->middleware) ? explode(',', $anno->middleware) : [];
                if (!empty($methodMids)) {
                    foreach ($methodMids as $v) {
                        if (empty($this->middleware[$v])) {
                            continue;
                        }
                        $handles[] = $this->middleware[$v];
                    }
                }
                $handleName = $method->name;
                $handles[] = $this->enableClosure ? function (Request $req, Response $res) use ($cont, $handleName) {
                    return $cont->$handleName($req, $res);
                } : "$className@$handleName";
                $group->addRouter([
                    'path' => $anno->path,
                    'methods' => $anno->method,
                    'handle' => $handles
                ]);
            }
        }
        return $group;
    }

    /**
     * 解析中间件组
     */
    public function handleMiddlewareGroup($path, $workspace)
    {
        if (!is_dir($path)) {
            return;
        }
        $dir = opendir($path);
        if ($dir === false) {
            throw new RouterException('can not open middleware directory ' . $dir);
            return [];
        }
        $middlewares = [];
        while (($file = readdir($dir)) !== false) {
            if ($file[0] == '.' || $file == 'vendor') {
                // 忽略隐藏、vendor目录
                continue;
            }
            $absulatePath = $path . '/' . $file;
            if (is_dir($absulatePath)) {
                $middlewares = array_merge($middlewares, $this->handleMiddlewareGroup($absulatePath, $workspace . '\\' . $file, $file));
                continue;
            }
            $middlewares = array_merge($middlewares, $this->handleMiddleware($absulatePath, $workspace));
        }
        return $middlewares;
    }

    /**
     * 解析中间件
     */
    public function handleMiddleware($path, $workspace)
    {
        $middlewares = [];
        $info = pathinfo($path);
        if ($info['extension'] !== 'php') {
            return $middlewares;
        }
        $className = $workspace . '\\' . $info['filename'];
        if (!class_exists($className)) {
            return $middlewares;
        }
        // 读取Middlewares注解
        $annotationReader = new AnnotationReader();
        $reflectionClass = new \ReflectionClass($className);
        $mgAnnotation = $annotationReader->getClassAnnotation($reflectionClass, Middlewares::class);
        if (empty($mgAnnotation)) {
            return $middlewares;
        }
        $methods = $reflectionClass->getMethods();
        if (empty($methods)) {
            return $middlewares;
        }
        $cont = $this->enableClosure ? new $className() : null;
        foreach ($methods as $method) {
            // 解析Middleware注解
            $mAnnotations = $annotationReader->getMethodAnnotations($method);
            foreach ($mAnnotations as $anno) {
                $handleName = $method->name;
                $middlewareName = $anno->name;
                if (get_class($anno) == Middleware::class) {
                    $middlewares[$middlewareName] = $this->enableClosure ? function (Request $req, Response $res) use ($cont, $handleName) {
                        return $cont->$handleName($req, $res);
                    } : "$className@$handleName";
                }
            }
        }
        return $middlewares;
    }
}
